顶层类只能处于Public跟默认访问级别. 而内部类可以处于任意访问级别.
A 实例内部类:
B 静态内部类:
局部内部类:
匿名类:
举例[实例内部类, 静态内部类]:
public class OnlyForTest { public static String staticOutMember = "Static Out Member"; private String userName; private int age; //省略Getter/Setter public static void main(String[] args) { OnlyForTest oft = new OnlyForTest(); //A1. 在创建实例内部类实例时, 外部类的实力必须已经存在. OnlyForTest.Inner inner = oft.new Inner(); //A3. 一个外部类可以有多个内部类 因此不允许外部类直接调用内部类方法或属性, 在需要时, 使用内部类实例访问内部类成员 //而内部类进对应一个外部类, 因此可以直接使用外部类的引用. oft.age = oft.new Inner2().age; inner.setOutInstanceNmae("tom"); oft.new Inner2().print(); //B1: 直接访问静态类静态成员, 不需创建实例. System.out.println(staticInnerClass.staticInnerMember); new staticInnerClass().printNmae(); } class Inner { public void setOutInstanceNmae(String s){ //A2. 实例内部类自动持有外部类实例的引用 userName = s; System.out.println("内部类直接引用外部类实例 - 内部类赋值外部类成员 " + userName); } }//end of class Inner class Inner2 { int age= 2; //A4. 在实例内部类中不能定义静态成员, 只能定义实例成员. //报错!static int staticInt = 4; public void print() { System.out.println(userName); //A5. 当内部类与外部类有同名的成员时,如age //直接使用age或shis.age表示inner2内age //OnlyForTest.this.age表示外部类中的age System.out.println(age); System.out.println(OnlyForTest.this.age); //B2. 直接访问外部类的静态成员 System.out.println(staticOutMember); } }//end of class Inner2 static class staticInnerClass { public static String staticInnerMember = "Static Inner Member"; public void printNmae() { //B3:静态内部类在访问外部类实例变量时, 必须通过外部实例 OnlyForTest o = new OnlyForTest(); o.userName = "set by Static Inner Class"; System.out.println(o.userName); } }//end of staticInnerClass }
编译后的Class文件:
DeCompiler之后 Inner的构造函数:
OnlyForTest$Inner2() { this$0 = OnlyForTest.this; super(); age = 2; }
可见, 每个inner实例都会自动包含一个外部类OnlyForTest的引用
而staticInnerClass类的构造函数为空, 因此他的实例不会自动引用外部类
从Decompile的Class文件来看, 对JVM来说, 无所谓内外类之分, 他们都是Class, 只是内部类会自动引用外部类实例 或是 其他特定的功能而已.
Login设计 |
当Request进入时, 先通过request.getSession(false)检查Session是否存在, 如果存在则表明已经登录,则通过sendRedirect(Calc.URL_MAIN)跳转到主页面 response.sendRedirect(); |
package com.insprise.servletStu; //省略import public class Login extends HttpServlet { public static final String USER_NAME = "userName"; public static final String PASS_WORD = "passWord"; private static final long serialVersionUID = 1L; private static final String INPUT_USER_NAME = "inputUserName"; private static final String INPUT_PASSWORD = "inputPassWord"; private String passWord; private String name; protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { name = (String) getServletContext().getAttribute(USER_NAME); passWord = (String) getServletContext().getAttribute(PASS_WORD); HttpSession session = request.getSession(false); if (session != null) { response.sendRedirect(Calc.URL_MAIN);//如果已经登录后再次打开登录页面, 跳转到主界面 return; } else { response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); PrintWriter out = response.getWriter(); if (request.getParameter(INPUT_USER_NAME) != null) { String inputUserName = request.getParameter(INPUT_USER_NAME); String inputPassWord = request.getParameter(INPUT_PASSWORD); if (inputUserName.equals(USER_NAME) && inputPassWord.equals(passWord)) { request.getSession().setAttribute(Calc.CURRENT_USER, USER_NAME); response.sendRedirect(Calc.URL_MAIN); return; } else { //登录失败 } } else { //省略若干println… } //省略doPost } |
CalcListener实现 |
该类实现ServletContextListener接口, 在Application初始化时, contextInitialized(ServletContextEvent sce)方法会被自动呼叫, 在此之后, Filter与Servlet开始初始化. 使用getServletContext().getInitParameter()方法获得web.xml中<context-param>标签中的内容. 使用ServletContext的setAttribute方法为获得用户名及密码, 并作为Attribute加入到ServletContext中 重要方法: ServletContext. setAttribute(); |
public class CalcListener implements ServletContextListener { private static Logger log = Logger.getLogger(CalcListener.class); public void contextInitialized(ServletContextEvent sce) { log.info("Context Started!" + sce.getServletContext()); ServletContext context = sce.getServletContext(); Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION)); Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION)); //读取到用户名及密码, 加入到Context Attribute中 context.setAttribute(Login.USER_NAME, new String(context.getInitParameter(Login.USER_NAME))); context.setAttribute(Login.PASS_WORD, new String(context.getInitParameter(Login.PASS_WORD))); } } |
2.4 抓图
实践
创建一个简单的计算器, 当用户输入整数时, 输出其平方值. 但用户需要登录才能进行操作, 用户的登录信息放在web.xml中, 每次登录用户可以使用该计算器三次, 三次使用结束后, 提示用户推出后重新登录继续使用.
User Case 用例 |
登录系统 |
||
Brief description |
登录系统 |
||
Scope/Level |
AthenaES/User goal |
Primary actor/role |
Root |
Minimal & Success Guarantees 保证 |
Minimal: The system logs how far it may get |
||
Preconditions 前提 |
无 |
||
Triggers 引发条件 |
用户打开登录界面 |
||
Main Success Scenario 成功场景 |
1. 系统提示用户输入用户名及密码 |
||
Extension 扩展 #1 |
3a: 系统无法核实用户身份, 提示其用户名或密码错误, 并提示用户重新输入 |
||
Notes and Issues |
无 |
User Case 用例 |
计算平方 |
||
Brief description |
计算平方 |
||
Scope/Level |
AthenaES/User goal |
Primary actor/role |
Root |
Minimal & Success Guarantees 保证 |
Minimal: The system logs how far it may get |
||
Preconditions 前提 |
Actor is logged in |
||
Triggers 引发条件 |
用户点击”计算” |
||
Main Success Scenario 成功场景 |
1. 确认用户使用次数未达到上限 |
||
Extension 扩展 #1 |
1a: 用户使用次数达到上限, 系统提示用户重新登陆后继续使用 |
||
Extension 扩展 #2 |
4a: 系统检测到用户输入不合法, 系统提示用户重新输入 用户重新进行2~4 |
||
Notes and Issues |
无 |
1. ServltContext层次的数据: 由于用户名及最大限制次数等信息在整个Servlet进程中都会被使用, 因此使用Context的Listener来实现.
2. Session层次的数据: 用户本次登录已使用次数应记录在Seesion中, 在当前Session中使用
3. Request层次的数据: 用户输入的数值尽在当前Request中使用
</>
Calc主页面实现: |
首先检查Session是否存在, 不存在则跳转到登录界面. |
package com.insprise.servletStu; //省略import public class Calc extends HttpServlet { public static final String MAX_CALCULATIONAS_PER_SESSION = “maxNumber”; // 最大使用次数的Key public static int MAX_CALCULATION_TIMES_PER_SESSION; // 最大使用次数 public static final String CURRENT_USER = “currentUser”; // 当前用户 public static final String URL_MAIN = “./Calc”; // 主页面 public static final String URL_LOGIN = “./Login”; // 登录页面 public static final String URL_LOGOUT = “./Logout”; // 注销页面 private static final String ACCESS_COUNT = “accessCount”; // 使用次数 private static final String INPUT_INT = “inputInt”; private static Logger log = Logger.getLogger(Calc.class); private static final int MAX_INT = 46340; // Math.sqrt(Integer.MAX_VALUE) private HttpSession session; private Integer accessCount; // 使用次数 public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { session = request.getSession(false); if (validatUser(session)) { // 已登录 accessCount = (Integer) session.getAttribute(ACCESS_COUNT); if (accessCount == null) { // 如果Session中不存在accessCount, 则表明其为新登录用户, // 赋值为0; accessCount = 0; session.setAttribute(ACCESS_COUNT, accessCount); } if (checkUseTimes(accessCount)) { // 已登录, 且使用次数在限制之内, 进行操作. proccessRequet(request, response); } else { // 已登录, 但使用次数操过限制,提示用户重新登陆. accessOverTime(request, response); } } else { // 未登录 response.sendRedirect(URL_LOGIN); } } public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException { doGet(request, response); } // 检查用户是否已登录 private boolean validatUser(HttpSession s) { if (session != null) { return true; } return false; } // 检查用户使用次数是否超过限制 private boolean checkUseTimes(Integer number) { //省略 } // 使用次数超过限制之后 提示其登录; private void accessOverTime(HttpServletRequest request, HttpServletResponse response) throws IOException { request.getSession().invalidate(); response.getWriter().println("本次登录已使用完毕, 请重新登录"); return; } // 获得输入数值, 计算结果, 返回信息 private void proccessRequet(HttpServletRequest request, HttpServletResponse response) throws IOException { Integer i; String title = "求整数平方"; response.setContentType("text/html"); response.setCharacterEncoding("utf-8"); // 不使用缓存 在点击推出后 按后退按钮时 不会出现缓存中的内容. response.setHeader("Cache-Control", "no-store"); response.setDateHeader("Expires", 0); response.setHeader("Pragma", "no-cache"); PrintWriter out = response.getWriter(); out.println(ServletUtilities.headwithTitle(title)); out.println("" + title + "
"); out.println("当前用户: " + session.getAttribute(CURRENT_USER) + " [退出]
"); String numberString = request.getParameter(INPUT_INT); if (numberString != null) { try { out.println("" + i + " 的平方是: " + calculator(i) + "
"); log.info("累计第[" + accessCount + "]次使用, 本次计算的参数为: " + i); } catch (Exception e) { out.println("您可能输入的不是整数 或您输入的整数绝对值过大请输入: 一个绝对值小于" + MAX_INT + " 的整数
"); } } out.println("您总共可以使用 " + MAX_CALCULATION_TIMES_PER_SESSION + "次, 您已累计使用:" + accessCount + "次
"); // 最后一次运行时, 停止输出Form; if (accessCount.equals(MAX_CALCULATION_TIMES_PER_SESSION)) { out.println("已达到最大使用次数限制, 请退出后重新登陆"); return; } printForm(out); out.println(""); } // 计算 private int calculator(int i) { accessCount = new Integer(accessCount.intValue() + 1); // 使用次数增加一次 session.setAttribute("accessCount", accessCount); return i * i; } // 打印一个Form private void printForm(PrintWriter out) { out.print("
很简单的几个概念, 其他还是OO.
request中获取参数
方法 |
说明 |
获得request中参数名name对应的值, 如果不存在该参数, 则返回Null – 注意: 名称大小写敏感 |
|
GetParameterNames |
返回一个枚举对象, 包含了所在request中的所有参数名, 如果request没有参数, 该方法返回一个空的枚举 |
GetParameterValues(String name) |
返回一个Sting[], 包含参数所对应的所有的值, 如果参数不存在,则返回Null. |
getParameterMap |
返回一个Map对象, 以参数名称为Key(String), 参数对应值为Value(类型String[]) |
getSession() |
获得当前Session |
Response中输出信息
方法 |
说明 |
setContentType(String type) |
声明返回数据的MIME类型 |
setCharacterEncoding(String charset) |
声明Response的编码 |
getWriter(); |
返回一个PrintWriter, 可通过该对象的print 或printlin方法向客户端发送字符串数据流 |
sendRedirect(String location) |
设置转向 – 转到某地址 |
方法 |
说明 |
getServletContext() |
获得Session所在的ServletContext对象 |
getAttribute(String name) |
返回该Session中name对应的Attribute对象, 如果没有则返回null |
getAttributeNames() |
返回一个枚举, 包括Seesion中所有的Attribut 对应的name, |
setAttribute(String name, Object value) |
在Session中加入Attribute |
invalidate() |
销毁Session |
方法 |
说明 |
getInitParameter(String name) |
返回一个String, 包含对应的初始化参数, 如果不存在则返回null, init参数可在web.xml中使用<context-param>设定 |
getInitParameterNames() |
返回由所有的init参数名称组成的枚举, 如果不存在任何init参数, 则返回空的枚举 |
getAttribute(String name) |
返回运行时设定相应的Attribute 对象, 如不存在则返回null |
getAttributeNames() |
返回一个枚举, 包含所有Attribute的name, 如果不存在任何Attribue, 则返回一个空的枚举 |
setAttribute(String name, Object object) |
在运行时增加Attribute |
removeAttribute(String name) |
删除对应name的Attribute |
Servlet通过IoC(Inversion of Control意为控制反转)将代码交给框架来执行. 因此只需要在Servlet类中扩展HttpServlet, 覆盖doGet, doPost等方法, 就可成功创建并执行一个Servlet. 在有客户Request时, 将由Service方法分析Request, 并交由对应方法处理, 如Get交由doGet方法处理.
Listener: Servlet中的listener类似于AS中的Event kind, 在Listener支持的事件 – 如Servlet初始化完毕, Session初始化, Request Destory,等, 在这些事件发生后, 将自动运行相应的方法(IoC)
如果需要Listener或Filter, 则在编写好相应的类之后, 加入到web.xml描述文件中.
Servlet 的数据层次:
1. ServletContext: Application初始化时会呼叫Listener contextInitialized(), 可以在该方法中获得或设定ServletContext生命周期内的数据. 如可以通过getInitParameter来获取web.xml中定义好的初始化参数, 也可以通过setAttribute方法设定Attribute, 这些Attribute将在整个Servlet生命周期内有效.
2. HttpSession: 当获得Session之后, 可以进行setAttribute等方法设定Session’的Attribute – 在该Session中有效
3. HttpRequest: 在doGet, doPost等方法中, 可以通过request. get Parameter(String name)等方法来获得request中的数据, 这些数据仅对当前的这个Request有效
事由: 最近几天看了点Servlet, 打算写一个小程序, 需要在application启动时从web.xml中加载一个参数, 由于仅在Login这个Servlet中使用, 于是我是这样实现的:
Login Login com.insprise.servletstu.Login maxNumber maxNumber 3 Login /Login
然后在Login中重写init()方法, 如下:
Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(this.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION));
功能以实现, 将最大值从web.xml中读取出来, 赋值给一个Global的变量.
===============================承上启下的分割线==============================================
但随着学习的深入, 发现容器并不一定会保持Servlet一直存活, 也许会在Request介绍后Destory掉Login Servlet, 然后如果再次Request Login, 则会重新init(), 于是重新读取web.xml, 重新赋值–明显第一次有效的, 后面的都是无用功.
从逻辑上分析, 该最大值应属于ServletContext级别的数据, 于是应使用Listener, 监听Application初始化, 在Application初始化时读取.
于是我没有改动web.xml, 设置了监听函数:
public void contextInitialized(ServletContextEvent sce) { log.info("Context Started!" + sce.getServletContext()); ServletContext context = sce.getServletContext(); Calc.MAX_CALCULATION_TIMES_PER_SESSION = Integer.parseInt(context.getInitParameter(Calc.MAX_CALCULATIONAS_PER_SESSION)); }
但不论如何都会有NullPoint的错误. 于是意识到该应在web.xml使用<context-param>设定参数, 而非某个<Servlet>下的<init-param>标签. 修改后的web.xml:
maxNumber 3
=====================================总结性的分割线============================================
总而言之:
使用ServletContext.getInitParameter()方法 可以获得web.xml中的<context-param>参数 – 使用范围很广, 能getServletContext就可以使用.
在Servlet的init()函数中使用this.getInitParameter()获得web.xml的<init-param>参数 – 较为局限的参数. 注意逻辑上的层次划分
// Proudly powered by Apache, PHP, MySQL, WordPress, Bootstrap, etc,.